/**
 * After update, the item is added to the end of the list
 */
//@author A0085082E
package logic;

import java.util.ArrayList;
import java.util.Calendar;

import memory.GenericEvents;
import memory.Memory;

public class Update {

	
	private static final String TYPE_EVENT = "Event";
	private static final String TYPE_DEADLINE = "Deadline";
	private static final String TYPE_TASK = "Task";

	private static final String ERROR_ITEM_NOT_FOUND = "Item not found: ";
	private static final String ERROR_MULTIPLE_ITEMS_FOUND_WITH_IDENTICAL_NAME = "Multiple items found with identical name: ";
	private static final String ERROR_END_IS_BEFORE_START = "Unable to update before start date/time is after end date/time";

	private static final int UPDATE_NAME = 34;
	private static final int UPDATE_DESCRIPTION = 35;
	private static final int UPDATE_START_TIME = 36;
	private static final int UPDATE_END_TIME = 37;
	private static final int UPDATE_TIME = 38;
	private static final int UPDATE_START_DATE = 39;
	private static final int UPDATE_END_DATE = 40;
	private static final int UPDATE_DATE = 41;

	private static GenericEvents task;
	private static String taskName;
	private static int type;
	private static Calendar dateTime;
	private static String newInfo;
	private static boolean setProper;

	/*
	 * Update item which the user provide the exact name
	 */
	public static void updateExact(String name, int newType, Calendar info,
			String descrip, Memory memory) {

		assert memory != null;
		initialiseVariables(name, newType, info, descrip);
		setTaskAndUpdate(memory);
		sortAndAdvanceHistory(memory);
	}

	/*
	 * Update item with index from a list after a search
	 */
	public static void updateAfterSearch(String name, int newType,
			Calendar info, String descript, int index, Memory memory) {
		initialiseVariables(name, newType, info, descript);

		setTaskItem(findItem(index, memory));
		checkIfEndIsBeforeStart();
		checkIfVariableIsSetProper_UpdateAfterSearch(memory);

	}

	/*
	 * Update item that is in detailed view
	 */
	public static void updateInDetailed(String name, int newType, Calendar info, String descrip, Memory memory){
		initialiseVariables(name, newType, info, descrip);
		setTaskItem(Display.detailedView);
		checkIfEndIsBeforeStart();
		checkIfVariableIsSetProper_UpdateInDetailed(memory);
	}

	
	/*
	 * Update the item, add to memory and display item to user in details
	 */
	private static void updateThis(GenericEvents item, int type,
			Calendar dateTime, String newInfo, Memory memory) {
		updateByAttribute(item, type, dateTime, newInfo);
		memory.add(item);
		Display.displayDetailed(item);
	}

	/*
	 * Check type for the type of attribute to be updated and
	 * update the attribute accordingly
	 */
	private static void updateByAttribute(GenericEvents item, int type,
			Calendar dateTime, String newInfo) {
		switch (type) {

		case UPDATE_NAME:
			updateTaskName(item, newInfo);
			break;

		case UPDATE_DESCRIPTION:
			updateDescription(item, newInfo);
			break;

		case UPDATE_TIME:
		case UPDATE_END_TIME:
			updateEndTime(item, dateTime);
			updateType(item);
			break;

		case UPDATE_START_TIME:
			updateStartTime(item, dateTime);
			updateType(item);
			break;

		case UPDATE_START_DATE:
			updateStartDate(item, dateTime);
			updateType(item);
			break;

		case UPDATE_END_DATE:
		case UPDATE_DATE:
			updateEndDate(item, dateTime);
			updateType(item);
			break;
		}


	}

	/*
	 * Update task name
	 */
	private static void updateTaskName(GenericEvents item, String name) {
		item.setName(name);
	}

	/*
	 * Update description of the item
	 */
	private static void updateDescription(GenericEvents item, String name) {
		item.setDescription(name);
	}

	/*
	 * Update start time
	 * 
	 * Initialise start date and time if start date and time has never been set.
	 * Start date is default at the day of the update
	 * If end date and time has never been set as well
	 * 
	 * Initialise end time as the same time of the start time set with this update
	 * End date is default at the day of the update
	 */
	private static void updateStartTime(GenericEvents item, Calendar time) {
		if (item.getStart() == null) {
			item.initStart();
		}

		if (item.getEnd() == null){
			updateEndTime(item, time);
		}

		item.setStartTime(time.get(Calendar.HOUR_OF_DAY),
				time.get(Calendar.MINUTE));
	}

	/*
	 * Update end time
	 * Initialise end date and time if end date/time has never been set.
	 * End date is default at the day of update
	 */
	private static void updateEndTime(GenericEvents item, Calendar time) {
		if (item.getEnd() == null) {
			item.initEnd();
		}
		item.setEndTime(time.get(Calendar.HOUR_OF_DAY),
				time.get(Calendar.MINUTE));
	}

	/*
	 * Update start date to the item
	 * Initialise the startdate if it was null
	 * Set start time default at 0000 if start date was initialised 
	 */
	private static void updateStartDate(GenericEvents item, Calendar time) {
		if (item.getStart() == null) {
			item.initStart();
			item.setStartTime(0, 0, 0);
		}

		if (item.getEnd() == null){
			updateEndDate(item, time); 
			item.setEndTime(23, 59, 59);
		}

		item.setStartDate(time.get(Calendar.YEAR), time.get(Calendar.MONTH),
				time.get(Calendar.DATE));
	}

	/*
	 * Update end date to the item
	 * Initialise the enddate if it was null
	 * Set end time default at 2359 if start date was initialised
	 */
	private static void updateEndDate(GenericEvents item, Calendar time) {
		if (item.getEnd() == null) {
			item.initEnd();
			item.setEndTime(23, 59, 59);
		}

		item.setEndDate(time.get(Calendar.YEAR), time.get(Calendar.MONTH),
				time.get(Calendar.DATE));
	}

	
	/*
	 * Initialise variables
	 */
	private static void initialiseVariables(String name, int newType, Calendar info,
			String descrip) {
		taskName = name;
		type = newType;
		dateTime = info;
		newInfo = descrip;
		setProper = true;
	}

	/*
	 * Check if the variables is set proper for update in detailed view
	 */
	private static void checkIfVariableIsSetProper_UpdateInDetailed(
			Memory memory) {
		if(setProper){
			removeItemAndUpdate_Detailed(memory);
		}else{
			assert setProper == false;
			System.out.println(ERROR_END_IS_BEFORE_START);
		}
	}

	/*
	 * remove item and update in detailed mode
	 */
	private static void removeItemAndUpdate_Detailed(Memory memory) {
		memory.remove(Display.detailedView);
		updateThis(task, type, dateTime, newInfo, memory);
		sortAndAdvanceHistory(memory);
	}

	
	
	/*
	 * Check if there is an error with date/time variable to be updated
	 * If there's no error, update the item
	 */
	private static void checkIfVariableIsSetProper_UpdateAfterSearch(Memory memory){
		if(!setProper){
			System.out.println(ERROR_END_IS_BEFORE_START);
		}else{
			updateItemAfterSearch(memory);
		}
	}

	/*
	 * Remove item from memory.
	 * Update the same item details and readd back into memory
	 */
	private static void updateItemAfterSearch(Memory memory){
		memory.remove(task);
		updateThis(task, type, dateTime, newInfo, memory);

	}

	/*
	 * Method find the item with the taskName that user wants to update
	 * Send user an error message if more than 1 item with same name is found
	 * Send user an error message if no item is found
	 */
	private static void setTaskAndUpdate(Memory memory) {
		ArrayList<GenericEvents> searchResult = Find.searchExact(taskName, memory);
		if (searchResult.size() == 1) {
			searchAndDelete(searchResult, memory);
		} else if (searchResult.size() > 1) {
			System.out.println(ERROR_MULTIPLE_ITEMS_FOUND_WITH_IDENTICAL_NAME
					+ taskName);
		} else {
			assert searchResult.size() <= 0;
			System.out.println(ERROR_ITEM_NOT_FOUND + taskName);
		}
	}

	/*
	 * Search item from the list, and delete from memory
	 */
	private static void searchAndDelete(ArrayList<GenericEvents> searchResult, Memory memory) {
		setTaskItem(searchResult.get(0));
		checkIfEndIsBeforeStart();
		if(setProper){
			memory.remove(searchResult.get(0));
			updateThis(task, type, dateTime, newInfo, memory);
		}else{
			assert setProper == false;
			System.out.println(ERROR_END_IS_BEFORE_START);
		}
	}

	/*
	 * Find the item with the index in memory
	 */
	private static GenericEvents findItem(int index, Memory memory) {
		GenericEvents item = Display.listView.get(index);
		return item;
	}

	/*
	 * Method checks if the end date is before start date
	 * Prints error message when updating startdate to after end date
	 * Or when updating enddate to before startdate
	 */
	private static void checkIfEndIsBeforeStart(){
		if(updateIsTimeOrDate()){
			if(isUpdateStartDateOrTime()){
				errorIfStartDateIsUpdatedToAfterEndDate();
			}else if(isUpdateEndDateOrTime()){
				errorIfEndDateIsUpdatedToBeforeStartDate();
			}
		}
	}

	private static void sortAndAdvanceHistory(Memory memory){
		Sort.sort(memory.getList());
		History.advance(memory);
	}

	private static boolean updateIsTimeOrDate(){
		return UPDATE_START_TIME <=type|| type >= UPDATE_DATE;
	}

	private static boolean isUpdateStartDateOrTime(){
		return (type == UPDATE_START_TIME|| type == UPDATE_START_DATE)&&task.getEnd() != null;
	}

	private static boolean isUpdateEndDateOrTime(){
		return (type == UPDATE_TIME||
				type == UPDATE_END_DATE||
				type == UPDATE_END_TIME||
				type == UPDATE_DATE) && task.getStart() != null; 
	}


	private static void errorIfStartDateIsUpdatedToAfterEndDate(){
		if(checkEndDateIsBeforeStartDate(dateTime, task.getEnd())){
			error();
		}
	}

	private static void errorIfEndDateIsUpdatedToBeforeStartDate(){
		if(checkEndDateIsBeforeStartDate(task.getStart(), dateTime)){
			error();
		}
	}

	private static Boolean checkEndDateIsBeforeStartDate(Calendar start, Calendar end){
		return end.before(start);
	}

	/*
	 * Change the type of the item after update
	 */
	private static void updateType(GenericEvents item){
		if(itemTypeIsTask(item)){
			item.setType(TYPE_TASK);
		}else if(itemTypeIsDeadline(item)){
			item.setType(TYPE_DEADLINE);
		}else if(itemTypeIsEvent(item)){
			item.setType(TYPE_EVENT);
		}
	}

	private static boolean itemTypeIsTask(GenericEvents item){
		return item.getStart() == null && item.getEnd() == null;
	}

	private static boolean itemTypeIsDeadline(GenericEvents item){
		return item.getStart() == null && item.getEnd() != null;
	}

	private static boolean itemTypeIsEvent(GenericEvents item){
		return item.getStart() != null;
	}

	private static void error(){
		setProper = false;
	}

	private static void setTaskItem(GenericEvents item) {
		task = item;
	}





}
